﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DotNetCommon.Extensions
{
    /// <summary>
    /// <seealso cref="Enum"/> 扩展类
    /// </summary>
    public static class EnumExtensions
    {
        /// <summary>
        /// 将枚举转为由code、desc键值对组成的集合,<para></para>
        /// 如: EnumTest{ [Description("A描述")]A, B, C}，默认转换为: <para></para>
        /// {<para></para>
        /// {0,"A描述"},<para></para>
        /// {1,"B"},<para></para>
        /// {2,"C"}<para></para>
        /// }<para></para>
        /// </summary>
        /// <param name="item">枚举项</param>
        /// <param name="useNameForKeyIfNoDescription">当枚举项上不存在[Description]特性时,是否使用枚举的Key值作为描述</param>
        /// <remarks>枚举的code是数字，key是定义时的名称，desc是[Description]中的值</remarks>
        /// <returns></returns>
        public static List<KeyValuePair<int, string>> ToCodeDescriptionList(this System.Enum item, bool useNameForKeyIfNoDescription = true)
        {
            return item.ToCodeKeyDescriptionList(useNameForKeyIfNoDescription).Select(i => new KeyValuePair<int, string>(i.code, i.desc)).ToList();
        }

        /// <summary>
        /// 将枚举转为由code、key键值对组成的集合,<para></para>
        /// 如: EnumTest{ [Description("A描述")]A, B, C}，默认转换为: <para></para>
        /// {<para></para>
        /// {0,"A"},<para></para>
        /// {1,"B"},<para></para>
        /// {2,"C"}<para></para>
        /// }<para></para>
        /// </summary>
        /// <param name="item">枚举项</param>
        /// <remarks>枚举的code是数字，key是定义时的名称，desc是[Description]中的值</remarks>
        /// <returns></returns>
        public static List<KeyValuePair<int, string>> ToCodeKeyList(this System.Enum item)
        {
            return item.ToCodeKeyDescriptionList(false).Select(i => new KeyValuePair<int, string>(i.code, i.key)).ToList();
        }

        /// <summary>
        /// 将枚举转为由key、code键值对组成的集合,<para></para>
        /// 如: EnumTest{ [Description("A描述")]A, B, C}，默认转换为: <para></para>
        /// {<para></para>
        /// {"A",0},<para></para>
        /// {"B",1},<para></para>
        /// {"C",2}<para></para>
        /// }<para></para>
        /// </summary>
        /// <remarks>枚举的code是数字，key是定义时的名称，desc是[Description]中的值</remarks>
        public static List<KeyValuePair<string, int>> ToKeyCodeList(this System.Enum item)
        {
            return item.ToCodeKeyDescriptionList(false).Select(i => new KeyValuePair<string, int>(i.key, i.code)).ToList();
        }

        /// <summary>
        /// 将枚举转为由key、desc键值对组成的集合,<para></para>
        /// 如: EnumTest{ [Description("A描述")]A, B, C}，默认转换为: <para></para>
        /// {<para></para>
        /// {"A","A描述"},<para></para>
        /// {"B","B"},<para></para>
        /// {"C","C"}<para></para>
        /// }<para></para>
        /// </summary>
        /// <param name="item">枚举项</param>
        /// <param name="useNameForKeyIfNoDescription">当枚举项上不存在[Description]特性时,是否使用枚举的key值作为描述</param>
        /// <remarks>枚举的code是数字，key是定义时的名称，desc是[Description]中的值</remarks>
        /// <returns></returns>
        public static List<KeyValuePair<string, string>> ToKeyDescriptionList(this System.Enum item, bool useNameForKeyIfNoDescription = true)
        {
            return item.ToCodeKeyDescriptionList(useNameForKeyIfNoDescription).Select(i => new KeyValuePair<string, string>(i.key, i.desc)).ToList();
        }


        /// <summary>
        /// 将枚举转为由code、key、desc组成元组的集合,<para></para>
        /// 如: EnumTest{ [Description("A描述")]A, B, C}，默认转换为: <para></para>
        /// {<para></para>
        /// (0,"A","A描述"),<para></para>
        /// (1,"B","B"),<para></para>
        /// (2,"C","C")<para></para>
        /// }<para></para>
        /// </summary>
        /// <param name="item">枚举项</param>
        /// <param name="useNameForKeyIfNoDescription">当枚举项上不存在[Description]特性时,是否使用枚举的key值作为描述</param>
        /// <remarks>枚举的code是数字，key是定义时的名称，desc是[Description]中的值</remarks>
        /// <returns></returns>
        public static List<(int code, string key, string desc)> ToCodeKeyDescriptionList(this System.Enum item, bool useNameForKeyIfNoDescription = true)
        {
            List<(int code, string key, string desc)> list = new List<(int code, string key, string desc)>();

            Type enumType = item.GetType();
            string[] fieldstrs = System.Enum.GetNames(enumType);
            foreach (var name in fieldstrs)
            {
                var fieldItem = enumType.GetField(name);
                list.Add((
                            fieldItem.GetRawConstantValue().ToWithCustomFallback(0),
                            fieldItem.Name,
                            fieldItem.GetCustomAttribute<DescriptionAttribute>()?.Description ?? (useNameForKeyIfNoDescription ? fieldItem.Name : "")
                        ));
            }
            return list;
        }

        /// <summary>
        /// 返回枚举值描述,优先从<seealso cref="System.ComponentModel.DescriptionAttribute"/>特性中获取,支持位枚举
        /// </summary>
        /// <param name="item">枚举项</param>
        /// <param name="useNameForKeyIfNoDescription">当枚举项上不存在[Description]特性时,是否使用枚举的key值作为描述</param>
        /// <returns></returns>
        public static string ToDescription(this Enum item, bool useNameForKeyIfNoDescription = true)
        {
            var desc = "";
            string name = item.ToString();
            var type = item.GetType();
            if (type.CustomAttributes.FirstOrDefault(_ => _.AttributeType == typeof(FlagsAttribute)) != null)
            {
                //位枚举
                var fields = type.GetFields().ToList();
                foreach (var field in fields)
                {
                    if (field.Name == "value__") continue;
                    if (item.HasFlag((Enum)Enum.Parse(type, field.Name)))
                    {
                        var t = item.GetType().GetField(field.Name)?.GetCustomAttribute<DescriptionAttribute>()?.Description ?? (useNameForKeyIfNoDescription ? field.Name : "");
                        if (string.IsNullOrWhiteSpace(desc)) desc = t;
                        else desc += ", " + t;
                    }
                }
            }
            else
            {
                //普通枚举
                desc = item.GetType().GetField(name)?.GetCustomAttribute<DescriptionAttribute>()?.Description ?? (useNameForKeyIfNoDescription ? name : "");
            }

            return desc ?? name;
        }

        /// <summary>
        /// 复核枚举值是否包含某一项
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enu"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public static bool Contains<T>(this Enum enu, T item) where T : Enum
        {
            if (enu == null) return false;
            return (item.To<int>() & enu.To<int>()) != 0;
        }
    }
}
